﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.Common;
using CoreEx.Common.Extensions;
using CoreEx.Common.Proxy;
using CoreEx.Services;
using LinFu.IoC;
using LinFu.IoC.Interfaces;
using LinFu.IoC.Configuration;
using CoreEx.MicroMapper;
namespace CoreEx.Database.Client
{
    public static class ConnectionFactory
    {
        private static IServiceContainer _serviceContainer;


        public static IServiceContainer ServiceContainer
        {
            get { return _serviceContainer; }
            set { _serviceContainer = value; }
        }


        static ConnectionFactory()
        {
            _serviceContainer = ServiceLocator.Container;            
        }

       

         
        



        #region IDbCommand

        public static IDbCommand AddParameter(this IDbCommand dbCommand, string name,object value ,DbType dbType,ParameterDirection direction)
        {
            var dataParameter = dbCommand.CreateParameter();
            dataParameter.ParameterName = name;
            dataParameter.DbType = dbType;
            dataParameter.Value = value;
            dataParameter.Direction = direction;
            dbCommand.Parameters.Add(dataParameter);
            return dbCommand;
        }
        public static IDbCommand AddParameter(this IDbCommand dbCommand, string name, object value,DbType dbType)
        {
            return AddParameter(dbCommand, name, value,dbType, ParameterDirection.InputOutput);
        }

        public static IDbCommand AddParameter(this IDbCommand dbCommand, string name,object value)
        {
            return AddParameter(dbCommand, name, value,DbType.Object, ParameterDirection.InputOutput);
        }

       

        public static IDataReader ExecuteReader(this IDbCommand dbCommand, string procedureName, params object[] arguments)
        {
            dbCommand.CommandText = procedureName;
            dbCommand.CommandType = CommandType.StoredProcedure;
            FillParameters(dbCommand,arguments);
            return dbCommand.ExecuteReader();            
        }

        private static void FillParameters(IDbCommand dbCommand, object[] arguments)
        {
            var inputParameters = dbCommand.Parameters.Cast<IDbDataParameter>().Where(
                p => p.Direction == ParameterDirection.Input || p.Direction == ParameterDirection.InputOutput).ToArray();
            for (int i = 0; i < inputParameters.Length; i++)
            {
                inputParameters[i].Value = arguments[i];
            }
        }


        public static object ExecuteScalar(this IDbCommand dbCommand, string procedureName, params object[] arguments)
        {
            dbCommand.CommandText = procedureName;
            dbCommand.CommandType = CommandType.StoredProcedure;
            FillParameters(dbCommand, arguments);
            return dbCommand.ExecuteScalar();
        }




        public static object ExecuteScalar(this IDbCommand dbCommand, string commandText)
        {
            dbCommand.CommandText = commandText;
            var result = dbCommand.ExecuteScalar();
            return result;
        }

        public static DataTable ExecuteDataTable(this IDbCommand dbCommand,string commandText)
        {
            dbCommand.CommandText = commandText;
            return ExecuteDataTable(dbCommand);
        }

        public static void ExecuteDataSet(this IDbCommand dbCommand,DataSet dataSet)
        {
            ExecuteDataSet(dbCommand,dataSet,dataSet.Tables[0].TableName);
        }

        public static void ExecuteDataSet(this IDbCommand dbCommand, DataSet dataSet,string tableName)
        {
            var connectionName = ((IDbConnectionInfo) dbCommand.Connection).Name;
            IDbDataAdapter dataAdapter =
                _serviceContainer.GetService<DbProviderFactory>(connectionName).CreateDataAdapter();
            dataAdapter.TableMappings.Add("Table", tableName);
            dataAdapter.SelectCommand = (DbCommand)((IActualTarget)dbCommand).Instance;
            dataAdapter.Fill(dataSet);
        }        

        public static DataTable ExecuteDataTable(this IDbCommand dbCommand)
        {
            DataSet dataSet = new DataSet();
            var connectionName = ((IDbConnectionInfo)dbCommand.Connection).Name;
            IDbDataAdapter dataAdapter =
                _serviceContainer.GetService<DbProviderFactory>(connectionName).CreateDataAdapter(); 
            dataAdapter.SelectCommand = (DbCommand)((IActualTarget)dbCommand).Instance;            
            dataAdapter.Fill(dataSet);
            return dataSet.Tables[0];
        }        
        
        
        /// <summary>
        /// Returns the contents of the <paramref name="dataReader"/> as a <see cref="IList{T}"/> containing objects of type T.
        /// </summary>
        /// <typeparam name="T">The type of object to convert each <see cref="IDataRecord"/> into.</typeparam>
        /// <param name="dataReader">The data reader that contains the source data.</param>
        /// <returns>An <see cref="IList{T}"/> that contains the result of the data reader to object mapping.</returns>
        public static IList<T> ToList<T>(this IDataReader dataReader)
        {
            var table = dataReader.ToTable();
            var tableMapper = _serviceContainer.GetService<ITableMapper<T>>();
            return tableMapper.Apply(table).ToList();
        }
        
        
        
        #endregion





    }
}